{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "87ac0b47",
   "metadata": {},
   "source": [
    "# 原则五：迪米特法则（Law of Demeter）\n",
    "\n",
    "# 定义\n",
    "- You only ask for objects which you directly need.\n",
    "\n",
    "即：一个对象应该对尽可能少的对象有接触，也就是只接触那些真正需要接触的对象。\n",
    "\n",
    "## 定义解读\n",
    "- 迪米特法则也叫做最少知道原则（Least Know Principle）， 一个类应该只和它的成员变量，方法的输入，返回参数中的类作交流，而不应该引入其他的类（间接交流）。\n",
    "\n",
    "\n",
    "## 优点\n",
    "实践迪米特法则可以良好地降低类与类之间的耦合，减少类与类之间的关联程度，让类与类之间的协作更加直接。\n",
    "\n",
    "\n",
    "## 代码讲解\n",
    "下面通过一个简单的关于汽车的例子来讲解一下迪米特法则。\n",
    "\n",
    "### 需求点\n",
    "\n",
    "设计一个汽车类，包含汽车的品牌名称，引擎等成员变量。提供一个方法返回引擎的品牌名称。\n",
    "\n",
    "### 不好的设计"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "a319fcb3",
   "metadata": {},
   "outputs": [],
   "source": [
    "# Car类：\n",
    "\n",
    "class GasEngine(object):\n",
    "    def __init__(self, brandName):\n",
    "        self.brandName =brandName  # 引擎名\n",
    "        \n",
    "class Car(object):\n",
    "    def __init__(self, engine:GasEngine):\n",
    "        self.engine = engine\n",
    "        \n",
    "    def usingEngine(self):\n",
    "        return self.engine\n",
    "    \n",
    "class Client(object):\n",
    "    def findCarEngineBrandName(self, car:Car):\n",
    "        engine = car.engine\n",
    "        return engine.brandName\n",
    "\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "9cb2f918",
   "metadata": {},
   "source": [
    "上面的设计完成了需求，但是却违反了迪米特法则。原因是在客户端的findCarEngineBrandName:中引入了和入参（Car）和返回值（NSString）无关的GasEngine对象。增加了客户端与\n",
    "GasEngine的耦合。而这个耦合显然是不必要更是可以避免的。\n",
    "\n",
    "接下来我们看一下如何设计可以避免这种耦合：\n",
    "\n",
    "## 较好的设计\n",
    "\n",
    "同样是Car这个类，我们去掉原有的返回引擎对象的方法，而是增加一个直接返回引擎品牌名称的方法："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "ca77fe2f",
   "metadata": {},
   "outputs": [],
   "source": [
    "# Car类：\n",
    "\n",
    "class GasEngine(object):\n",
    "    def __init__(self, brandName):\n",
    "        self.brandName =brandName  # 引擎名\n",
    "        \n",
    "class Car(object):\n",
    "    def __init__(self, engine:GasEngine):\n",
    "        self.engine = engine\n",
    "        \n",
    "    def usingEngineBrandName(self):\n",
    "        return self.engine.brandName\n",
    "    \n",
    "class Client(object):\n",
    "    def findCarEngineBrandName(self, car:Car): \n",
    "        return engine.usingEngineBrandName"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "6707f047",
   "metadata": {},
   "source": [
    "与之前的设计不同，在客户端里面，没有引入GasEngine类，而是直接通过Car实例获取到了需要的数据。\n",
    "\n",
    "这样设计的好处是，如果这辆车的引擎换成了电动引擎(原来的GasEngine类换成了ElectricEngine类)，客户端代码可以不做任何修改！因为它没有引入任何引擎类，而是直接获取了引擎的品牌名称。\n",
    "\n",
    "所以在这种情况下我们只需要修改Car类的usingEngineBrandName方法实现，将新引擎的品牌名称返回即可。\n",
    "\n",
    "下面来看一下这两个设计的UML 类图，可以更形象地看出两种设计上的区别：\n",
    "\n",
    "## UML 类图对比\n",
    "\n",
    "未实践迪米特法则：\n",
    "\n",
    "![101](pic/105-101.png)\n",
    "\n",
    "实践了迪米特法则：\n",
    "![102](pic/105-102.png)\n",
    "\n",
    "\n",
    "很明显，在实践了迪米特法则的 UML 类图里面，没有了Client对GasEngine的依赖，耦合性降低。\n",
    "如何实践\n",
    "今后在做对象与对象之间交互的设计时，应该极力避免引出中间对象的情况（需要导入其他对象的类）：需要什么对象直接返回即可，降低类之间的耦合度。\n",
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "bca19421",
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "10be1b0f",
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.8.13"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
